//
//	HOME AUTOMATION GATEWAY PROJECT
//
//  (c) 2008 mocom software GmbH & Co KG
//	for European Microsoft Innovation Center
//
//  $Author: volker $
//  $Date: 2009-09-03 17:04:25 +0200 (Do, 03. Sep 2009) $
//  $Revision: 463 $
//
//  Microsoft dotNetMF Project
//  Copyright ©2001,2002,2003,2004 Microsoft Corporation
//  One Microsoft Way, Redmond, Washington 98052-6399 U.S.A.
//  All rights reserved.
//  MICROSOFT CONFIDENTIAL
//
//-----------------------------------------------------------------------------
#include <tinyhal.h>
#include "..\STNAND0x.h"
#include "nand_driver.h"

#pragma diag_suppress 186 // pointless comparison of unsigned integer with zero
#pragma diag_suppress 177 // variable "<name>" was declared but never referenced

#undef DEBUG
#define DEBUG 0

#if DEBUG
 #define DPRINTF		debug_printf
#else
 #define DPRINTF		null_printf
#endif

#ifdef USE_WEAR_LEVELING
#define BASE_OFFSET(x) 	((x) < FLASH_BASE_ADDRESS_ADJUSTED) ? ((x)+FLASH_BASE_ADDRESS_ADJUSTED) : (x)
#else
#define BASE_OFFSET(x) 	(x)
#endif

 void null_printf(char *fmt, ...)
 {
 }

//--//

extern STNAND0x_BS_Driver g_STNAND0x_BS_Driver;



//--//


BOOL STNAND0x_BS_Driver::ChipInitialize( void* context )
{
	BLOCK_CONFIG*	config = (BLOCK_CONFIG* )context;
    unsigned 		ManufacturerCode = 0;
    unsigned 		DeviceCode       = 0;
	char *			devname;

	g_STNAND0x_BS_Driver.devname = "-- no NAND found --";

	if (config && config->BlockDeviceInformation && config->BlockDeviceInformation->Regions && HALNandIfControllerInitialize()) {
		ReadProductID(context, ManufacturerCode, DeviceCode );

		if (FLASH_MANUFACTURER_CODE == ManufacturerCode)
		{
			switch (DeviceCode) {
 				case 0xa1:
					devname = "STNAND01GR3B2B"; 
					break;
				case 0xf1:
					devname = "STNAND01GW3B2B"; 
					break;
/* 				case 0xaa:
					devname = "STNAND02GR3B2B"; 
					break;
				case 0xda:
					devname = "STNAND02GW3B2B"; 
					break;
					*/
 				default:
					return FALSE;
			}

			g_STNAND0x_BS_Driver.devname = devname;
			
			// ERASE FILE SYSTEM
		/*	
			UINT32 k = (1024*128);
			UINT32 i = (0x43*k);
			while (EraseBlock(context,i))
				i += k;
			*/
			return TRUE;
		}
		return FALSE;
	}
	return FALSE;
}

BOOL STNAND0x_BS_Driver::ChipUnInitialize( void* context )
{
    return TRUE;
}

BOOL STNAND0x_BS_Driver::ReadProductID( void* context, FLASH_WORD& ManufacturerCode, FLASH_WORD& DeviceCode )
{
	unsigned m,d;
    HALNandIfControllerReadId(&m,&d);

	ManufacturerCode = m;
	DeviceCode= d;

    return TRUE;
}

const BlockDeviceInfo* STNAND0x_BS_Driver::GetDeviceInfo( void* context)
{
    BLOCK_CONFIG* config = (BLOCK_CONFIG* )context;

	//debug_printf( "STNAND0X: GetDeviceInfo [%s]\r\n",g_STNAND0x_BS_Driver.devname);
    return config->BlockDeviceInformation;
}

BOOL STNAND0x_BS_Driver::Read( void* context, ByteAddress byteAddr, UINT32 NumOfBytes, BYTE * pSectorBuff )
{
    NATIVE_PROFILE_HAL_DRIVERS_FLASH();
    UINT32 Offset;
	UINT32 Residue;
	UINT32 Sectors;
	UINT32 ToBeRead;
	UINT32 BytesPerSector;
	UINT32 RegionIndex, RangeIndex;
	UINT32 rlen;
	UINT8 *pBuf = pSectorBuff;
	
    ByteAddress StartSector;
	const BlockRegionInfo *pRegion;
	BLOCK_CONFIG* pConfig = (BLOCK_CONFIG* )context;

	//DPRINTF( "STNAND0X: ReadFrombyteAddr(0x%8.8lx,0x%8.8lx,%lu)\r\n",context, byteAddr, NumOfBytes);

    if (pSectorBuff == NULL || (NumOfBytes == 0))
		return FALSE;

	if (pConfig->BlockDeviceInformation->FindRegionFromAddress( BASE_OFFSET(byteAddr), RegionIndex, RangeIndex))
    {
        pRegion = &pConfig->BlockDeviceInformation->Regions[RegionIndex];
        BytesPerSector = pConfig->BlockDeviceInformation->BytesPerSector;
        StartSector = pConfig->BlockDeviceInformation->PhysicalToSectorAddress( pRegion, BASE_OFFSET(byteAddr) );

        // calc data for transfer
		//
		// a transfer is splited into "sectors" and "Residue"
		// Offset: 	number of bytes NOT read from the first sector
		// 				starting at sector boundary, below marked <o>
		// Sectors: 	number of plain full-length sectors
		// Residue: 	number of bytes to we read after all complete
		// 				sectors are written, below marked <r>
		// ToBeRead:	number of byte to be read from first sector, below
		//				marked <tbr>
		//
		//
		// --+----Sx----+---Sx+1---+---Sx+2---+---Sx+..--+---Sx+N---+---
		//   |          |          |          |          |          |
		// --+----------+----------+----------+----------+----------+---
		//   |   ^                                            ^
		//   |   +-byteAddr							  +-byteAddr+NumOfBytes
	    //	 | 	 |											  | 	
		//	 |<o>|											  | 	
		//   |   |<tbr->|<-(NumOfBytes-tbr)------------------>|
		// 	 |<Sectors---------------------------------->|    |
		// 												 |<r->|
		//
		Offset = ((byteAddr - pRegion->Start) % BytesPerSector);
		
		ToBeRead = BytesPerSector - Offset;
		if (ToBeRead >= NumOfBytes) {
			Sectors=0;
			Residue=NumOfBytes;
		} else {
			// calc number of bytes to read once ToBeRead is read.
			NumOfBytes -= ToBeRead;
			// get number of sectors
			Sectors = NumOfBytes / BytesPerSector;
			// get residue - maybe 0
			Residue = NumOfBytes - (Sectors*BytesPerSector);
			// add ToBeRead sector as that is what we start with.
			// note: ToBeRead can not be zero!
			Sectors++;
		}

		//DPRINTF( "STNAND0X: ToBeRead=%u Sectors=%u Residue=%u Offset=%u\r\n",ToBeRead,Sectors,Residue,Offset);

		while (Sectors--)
        {
			rlen = HALNandIfControllerReadSector(StartSector+FLASH_START_SECTOR,Offset,pBuf,ToBeRead);
			if (rlen != ToBeRead) {
				DPRINTF( "STNAND0X: ReadFrombyteAddr - FALSE in %u (%u!=%u)\r\n",__LINE__,rlen,ToBeRead);
				return FALSE;
			}
			// advance buffer start
            pBuf = &pBuf[ToBeRead];
			// next sector
			StartSector++;
            // after initial read, reset Offset to 0 and ToBeRead to sector size
            Offset = 0;
			ToBeRead = BytesPerSector;
        }

		// If last page is only a prtial read ..
		if (Residue) 
		{
			rlen = HALNandIfControllerReadSector(StartSector+FLASH_START_SECTOR,Offset,pBuf,Residue);
			if (rlen != Residue) 
			{
				DPRINTF( "STNAND0X: ReadFrombyteAddr - (R) FALSE in %u (%u!=%u)\r\n",__LINE__,rlen,Residue);
				return FALSE;
			}
		}
        return TRUE;
    }

	DPRINTF( "STNAND0X: ReadFrombyteAddr - FALSE in %u\r\n",__LINE__);
    return FALSE;
}

//
// Physical write funtions need to cache pages they want to write partial. That is the case
// when ever the starting address is not aligned to a sector boundary and/or the end of
// a block to be written is not aligned with a sector boundary. Both can happen at the same
// time.
// We MUST cahce the pages as the NAND internally use a buffer for write operations. This buffer
// will hold arbitrary data and a straight write with, for example, Offset would lead to
// unintended content in FLASH.
//
BOOL STNAND0x_BS_Driver::WriteInternal(void* context, ByteAddress byteAddr, UINT32 NumOfBytes, BYTE * pSectorBuff, BOOL ReadModifyWrite, BOOL fill)
{
    NATIVE_PROFILE_PAL_FLASH();

    UINT32 ToBeWritten;
    UINT32 BytesPerSector;
    UINT32 Offset;
	UINT32 RegionIndex, RangeIndex;
	UINT32 rlen;
	UINT8 *pBuf = pSectorBuff;
	
	static BYTE s_sectorBuff[2048];
	
    ByteAddress StartSector;
	const BlockRegionInfo *pRegion;
	
	BLOCK_CONFIG* pConfig = (BLOCK_CONFIG* )context;
    UINT32 usec_w = pConfig->BlockDeviceInformation->MaxSectorWrite_uSec;
	UINT32 usec_e = pConfig->BlockDeviceInformation->MaxBlockErase_uSec;

    if (pConfig->BlockDeviceInformation->FindRegionFromAddress( BASE_OFFSET(byteAddr), RegionIndex, RangeIndex))
    {
        pRegion = &pConfig->BlockDeviceInformation->Regions[RegionIndex];
        BytesPerSector = pConfig->BlockDeviceInformation->BytesPerSector;
        StartSector = pConfig->BlockDeviceInformation->PhysicalToSectorAddress( pRegion, BASE_OFFSET(byteAddr) );

		// calc data for transfer
		//
		// a transfer is splited into "sectors" and "Residue"
		// Offset: 		number of bytes NOT read from the first sector
		// 				starting at sector boundary, below marked <o>
		// Sectors: 	number of plain full-length sectors
		// Residue: 	number of bytes to we written after all complete
		// 				sectors are written, below marked <r>
		// ToBeWritten:	number of byte to be written into first sector, below
		//				marked <tbr>
		//
		//
		// --+----Sx----+---Sx+1---+---Sx+2---+---Sx+..--+---Sx+N---+---
		//   |          |          |          |          |          |
		// --+----------+----------+----------+----------+----------+---
		//   |   ^                                            ^
		//   |   +-byteAddr							  +-byteAddr+NumOfBytes
		//	 | 	 |
		//	 |<o>|
		//   |   |<tbr->|
		// 	 |<Sectors---------------------------------->|
		// 												 |<r->|
		//
		//	Difference to read is that we need to cache the first sector before write.
		//
		Offset = ((byteAddr - pRegion->Start) % BytesPerSector);
		DPRINTF( "STNAND0X: WriteTobyteAddr(0x%8.8lx,%lu,%lu,RMW=%s,FILL=%s)\r\n",byteAddr, NumOfBytes, Offset, ReadModifyWrite ? "YES" : "NO" ,fill ? "YES" : "NO");
		
		// bytes to be written in this round. this is 1..BytesPerSector
		ToBeWritten = BytesPerSector - Offset;
			
		// now, if ReadModifyWrite mode is TRUE: Cry load and clear that we give a shit!
		// it simply does not work as sectors can't be earsed, only blocks.
		if (ReadModifyWrite) 
		{
			// read sector to internal tmp buffer
			debug_printf( "STNAND0X: WriteInternal - ReadModifyWrite is not supported in line %u\r\n",__LINE__);
		}
				
		while (NumOfBytes) 
		{
			// if its more than NumOfBytes, cut it to NumOfBytes
			if (ToBeWritten > NumOfBytes) 
				ToBeWritten = NumOfBytes;
	
			// write sector, Offset is zero 
			rlen = HALNandIfControllerWriteSector(StartSector+FLASH_START_SECTOR,Offset,pBuf,ToBeWritten,usec_w,fill);				
			if (rlen != ToBeWritten) 
			{
				debug_printf( "STNAND0X: WriteInternal - write error in line %u\r\n",__LINE__);
				return FALSE;
			}
			// advance buffer pointer and adjust bytes to write
			pBuf = &pBuf[ToBeWritten];
			NumOfBytes -= ToBeWritten;
			// set Offset to zero and written now to full sector size
			Offset = 0;			
			ToBeWritten = BytesPerSector;
			// advance sector counter
			StartSector++;
		}			
		return TRUE;
	}
	
    debug_printf( "STNAND0X: WriteInternal - address not found (0x%8.8lx) in line %u\r\n",__LINE__);
    return FALSE;
}


BOOL STNAND0x_BS_Driver::IsBlockErased( void* context, ByteAddress SectorStart, UINT32 BlockLength  )
{
    NATIVE_PROFILE_HAL_DRIVERS_FLASH();

    UINT32 RegionIndex, RangeIndex;

    BLOCK_CONFIG* pConfig = (BLOCK_CONFIG*)context;

    if (!pConfig->BlockDeviceInformation->FindRegionFromAddress( BASE_OFFSET(SectorStart), RegionIndex, RangeIndex))
    {
        return FALSE;
    }

    const BlockRegionInfo* pRegion = &pConfig->BlockDeviceInformation->Regions[RegionIndex];

    // we can do this because we know the block length is 2^n
    ByteAddress StartSector = pConfig->BlockDeviceInformation->PhysicalToSectorAddress( pRegion, BASE_OFFSET(SectorStart) );
    UINT32 SectorsPerBlock  = pRegion->BytesPerBlock / pConfig->BlockDeviceInformation->BytesPerSector;
    
	DPRINTF( "STNAND0X: IsBlockErased %u? ",StartSector);

    for(int i=0; i<SectorsPerBlock;i++,StartSector++)
    {
		// read sector + spare
		if (!HALNandIfControllerIsErased(StartSector+FLASH_START_SECTOR)) {
			DPRINTF( "NO\r\n");
			return FALSE;
		}
    }

	DPRINTF( "YES\r\n");
    return TRUE;

}

BOOL STNAND0x_BS_Driver::GetSectorMetadata(void* context, ByteAddress SectorStart, SectorMetadata* pSectorMetadata)
{
    NATIVE_PROFILE_HAL_DRIVERS_FLASH();

    UINT32 RegionIndex, RangeIndex;

    BLOCK_CONFIG* pConfig = (BLOCK_CONFIG*)context;

    if (!pConfig->BlockDeviceInformation->FindRegionFromAddress( BASE_OFFSET(SectorStart), RegionIndex, RangeIndex))
    {
        return FALSE;
    }

    const BlockRegionInfo* pRegion = &pConfig->BlockDeviceInformation->Regions[RegionIndex];

    // we can do this because we know the block length is 2^n
    ByteAddress StartSector = pConfig->BlockDeviceInformation->PhysicalToSectorAddress( pRegion, BASE_OFFSET(SectorStart) );
    
    if (pSectorMetadata)  
	{
		return (HALNandIfControllerReadSpare(StartSector+FLASH_START_SECTOR, (UINT8 *)pSectorMetadata, sizeof(SectorMetadata)) == sizeof(SectorMetadata));
	}
	return FALSE;
}

BOOL STNAND0x_BS_Driver::SetSectorMetadata(void* context, ByteAddress SectorStart, SectorMetadata* pSectorMetadata)
{
    NATIVE_PROFILE_HAL_DRIVERS_FLASH();

    UINT32 RegionIndex, RangeIndex;

    BLOCK_CONFIG* pConfig = (BLOCK_CONFIG*)context;

    if (!pConfig->BlockDeviceInformation->FindRegionFromAddress( BASE_OFFSET(SectorStart), RegionIndex, RangeIndex))
    {
        return FALSE;
    }

    const BlockRegionInfo* pRegion = &pConfig->BlockDeviceInformation->Regions[RegionIndex];

    // we can do this because we know the block length is 2^n
    ByteAddress StartSector = pConfig->BlockDeviceInformation->PhysicalToSectorAddress( pRegion, BASE_OFFSET(SectorStart) );
    
    if (pSectorMetadata)  
	{
		return (HALNandIfControllerWriteSpare(StartSector+FLASH_START_SECTOR, (UINT8 *)pSectorMetadata, sizeof(SectorMetadata), pConfig->BlockDeviceInformation->MaxSectorWrite_uSec) == sizeof(SectorMetadata));
	}
	return FALSE;
}

BOOL STNAND0x_BS_Driver::Memset( void* context, ByteAddress byteAddr, UINT8 Data, UINT32 NumOfBytes )
{
	return WriteInternal(context, byteAddr, NumOfBytes, &Data, true, true);	
}

BOOL STNAND0x_BS_Driver::Write(void* context, ByteAddress byteAddr, UINT32 NumOfBytes, BYTE * pSectorBuff, BOOL ReadModifyWrite)
{
	return WriteInternal(context, byteAddr, NumOfBytes, pSectorBuff, ReadModifyWrite, false);	
}

BOOL STNAND0x_BS_Driver::EraseBlock( void* context, SectorAddress Sector )
{
    NATIVE_PROFILE_HAL_DRIVERS_FLASH();

    UINT32 RegionIndex, RangeIndex;
    
    BLOCK_CONFIG* pConfig = (BLOCK_CONFIG*)context;

    if (!pConfig->BlockDeviceInformation->FindRegionFromAddress( BASE_OFFSET(Sector), RegionIndex, RangeIndex))
    {
        return FALSE;
    }

	const BlockRegionInfo *pRegion = &pConfig->BlockDeviceInformation->Regions[RegionIndex];
			
    UINT32 usec 		= pConfig->BlockDeviceInformation->MaxBlockErase_uSec;    
	UINT32 StartSector 	= pConfig->BlockDeviceInformation->PhysicalToSectorAddress( pRegion, BASE_OFFSET(Sector) );

	DPRINTF( "STNAND0X: EraseBlock %u\r\n",StartSector);

	return HALNandIfControllerEraseBlock(StartSector+FLASH_START_SECTOR,usec) != 0;
}

void STNAND0x_BS_Driver::SetPowerState( void* context, BOOL State )
{
    // our flash driver is always Power ON
	DPRINTF( "STNAND0X: SetPowerState\r\n");
    return ;
}


UINT32 STNAND0x_BS_Driver::MaxSectorWrite_uSec( void* context )
{
    NATIVE_PROFILE_PAL_FLASH();

    BLOCK_CONFIG* config = (BLOCK_CONFIG* )context;

    return config->BlockDeviceInformation->MaxSectorWrite_uSec;
}

UINT32 STNAND0x_BS_Driver::MaxBlockErase_uSec( void* context )
{
    NATIVE_PROFILE_PAL_FLASH();

    BLOCK_CONFIG* config = (BLOCK_CONFIG* )context;

    return config->BlockDeviceInformation->MaxBlockErase_uSec;

}

struct IBlockStorageDevice g_STNAND0x_BS_DeviceTable =
{
    &STNAND0x_BS_Driver::ChipInitialize,
    &STNAND0x_BS_Driver::ChipUnInitialize,
    &STNAND0x_BS_Driver::GetDeviceInfo,
    &STNAND0x_BS_Driver::Read,
    &STNAND0x_BS_Driver::Write,
    &STNAND0x_BS_Driver::Memset,
    &STNAND0x_BS_Driver::GetSectorMetadata,
    &STNAND0x_BS_Driver::SetSectorMetadata,
    &STNAND0x_BS_Driver::IsBlockErased,
    &STNAND0x_BS_Driver::EraseBlock,
    &STNAND0x_BS_Driver::SetPowerState,
    &STNAND0x_BS_Driver::MaxSectorWrite_uSec,
    &STNAND0x_BS_Driver::MaxBlockErase_uSec,    
};

